package com.varian.tool;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.AntPathMatcher;
import cn.hutool.core.util.*;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.varian.tool.enums.BaseEnum;
import com.varian.tool.enums.EnvironmentProfileEnum;
import com.varian.tool.exception.ServerException;
import com.varian.tool.extend.Conditioner;
import com.varian.tool.extend.Selector;
import com.varian.tool.log.TraceIdContextHolder;
import com.varian.tool.result.R;
import com.varian.tool.tree.INode;
import com.varian.tool.tree.TreeNodeManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolation;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.env.Environment;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 判断器，可以进行条件判断，如果满足条件，执行后续操作
 *
 * @author ben
 * @since 2024/6/22
 */
@UtilityClass
public class Func {

    public final String TRACE_ID = "trace-id";
    public final Long TREE_ROOT_ID = 0L;
    public final Long ROOT_ID = 0L;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Getter
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    @Getter
    private final HandlerMethodArgumentResolverComposite methodArgumentResolver = new HandlerMethodArgumentResolverComposite();

    public EnvironmentProfileEnum getProfile() {
        Environment environment = SpringUtil.getApplicationContext().getEnvironment();
        String[] activeProfiles = ArrayUtil.defaultIfEmpty(environment.getActiveProfiles(), environment.getDefaultProfiles());
        return ArrayUtil.isNotEmpty(activeProfiles) ? EnvironmentProfileEnum.of(activeProfiles[0]) : null;
    }

    public boolean isDev() {
        return getProfile().equals(EnvironmentProfileEnum.DEV);
    }

    public void devOpt(Runnable runnable) {
        Optional.of(isDev()).ifPresent(b -> runnable.run());
    }

    public boolean isProd() {
        return getProfile().equals(EnvironmentProfileEnum.PROD);
    }

    public ObjectMapper getObjectMapper() {
        return SpringUtil.getBean(ObjectMapper.class);
    }

    public String jackson2JsonStr(Object o) {
        if (o == null) {
            return null;
        }
        return object2JsonStr(o);
    }

    private String object2JsonStr(Object o) {
        try {
            return getObjectMapper().writeValueAsString(o);
        } catch (JsonProcessingException e) {
            throw ServerException.of("Failed to serialize object to JSON string using Jackson: " + e.getMessage());
        }
    }

    public <P, R> Selector<P, R> selector(P param) {
        return Selector.of(param);
    }

    public <P> Conditioner<P> conditioner(P param) {
        return Conditioner.of(param);
    }

    /**
     * 通过func自定义一个规则，此规则将原集合中的元素转换成新的元素，生成新的列表返回<br>
     * 例如提供的是一个Bean列表，通过Function接口实现获取某个字段值，返回这个字段值组成的新列表
     *
     * @param <T>        集合元素类型
     * @param <R>        返回集合元素类型
     * @param collection 原集合
     * @param func       编辑函数
     * @param ignoreNull 是否忽略空值，这里的空值包括函数处理前和处理后的null值
     * @return 抽取后的新列表
     * @since 5.3.5
     */
    public <T, R> Set<R> mapSet(Iterable<T> collection, Function<T, R> func, boolean ignoreNull) {
        Set<R> fieldValueList = new HashSet<>();
        if (null == collection) {
            return fieldValueList;
        }
        R value;
        for (T t : collection) {
            if (null == t && ignoreNull) {
                continue;
            }
            value = func.apply(t);
            if (null == value && ignoreNull) {
                continue;
            }
            fieldValueList.add(value);
        }
        return fieldValueList;
    }

    /**
     * 根据包名和超类创建新的实例列表
     *
     * @param packageName 包名
     * @param superClass  超类
     * @return 新的实例列表
     */
    @SuppressWarnings("unchecked")
    public <T> List<T> newInstanceBySuper(String packageName, Class<T> superClass) {
        // 扫描指定包中符合超类条件的类集合
        Set<Class<?>> classes = ClassUtil.scanPackageBySuper(packageName, superClass);
        // 创建实例列表
        return (List<T>) CollUtil.map(classes, ReflectUtil::newInstance, true);
    }

    private ServletRequestAttributes getServletRequestAttributes() {
        return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    }

    public HttpServletRequest getServletRequest() {
        ServletRequestAttributes attributes = getServletRequestAttributes();
        return attributes != null ? attributes.getRequest() : null;
    }

    public HttpServletResponse getServletResponse() {
        ServletRequestAttributes attributes = getServletRequestAttributes();
        return attributes != null ? attributes.getResponse() : null;
    }

    /**
     * 判断给定的路径是否与指定的Ant风格路径模式匹配。
     *
     * @param pattern 指定的Ant风格路径模式。
     * @param path    需要匹配的路径。
     * @return 如果路径匹配模式，则返回true；否则返回false。
     */
    public boolean antPathMatch(String pattern, String path) {
        // 使用AntPathMatcher实例进行路径模式匹配
        return antPathMatcher.match(pattern, path);
    }

    public <K, V> Map<K, V> unmodifiableMap(List<V> list, Function<V, K> keyMapper) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyMap();
        }
        Objects.requireNonNull(keyMapper, "keyMapper");
        return list.stream().collect(Collectors.toUnmodifiableMap(keyMapper, Function.identity()));
    }

    public <T> List<T> unmodifiableList(List<T> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        return Collections.unmodifiableList(list);
    }

    public <P, R> List<R> convert(List<P> roles, Function<P, R> func) {
        if (CollUtil.isEmpty(roles)) {
            return Collections.emptyList();
        }
        return CollUtil.map(roles, func, true);
    }

    public <T extends INode<T>> List<T> treeBuild(List<T> nodeList) {
        if (CollUtil.isEmpty(nodeList)) {
            return Collections.emptyList();
        }
        return new TreeNodeManager<>(nodeList).build();
    }

    // 将树形结构平铺成列表
    public <T extends INode<T>> List<T> treeFlat(List<T> nodeList) {
        if (CollUtil.isEmpty(nodeList)) {
            return Collections.emptyList();
        }
        List<T> flatedList = ListUtil.list(false);
        doTreeFlat(nodeList, flatedList);
        return flatedList;
    }

    private <T extends INode<T>> void doTreeFlat(List<T> nodeList, List<T> result) {
        if (CollUtil.isEmpty(nodeList)) {
            return;
        }
        for (T node : nodeList) {
            result.add(node);
            doTreeFlat(node.getChildren(), result);
        }
    }

    public Class<?> getEnumType(Class<?> targetType) {
        Class<?> enumType = targetType;
        while (enumType != null && !enumType.isEnum()) {
            enumType = enumType.getSuperclass();
        }
        Assert.notNull(enumType, "The target type {} does not refer to an enum", targetType.getName());
        return enumType;
    }

    public <T extends BaseEnum<T>> T findBaseEnumByValue(T[] values, Integer value) {
        return Arrays.stream(values)
                .filter(v -> Objects.equals(v.getValue(), value))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException(StrUtil.format("value is not valid")));
    }

    public void validate(Object bean, Class<?>... groups) {
        Set<ConstraintViolation<Object>> validate = SpringUtil.getBean(LocalValidatorFactoryBean.class).validate(bean, groups);
        if (CollUtil.isNotEmpty(validate)) {
            throw new IllegalArgumentException(CollUtil.getFirst(validate).getMessage());
        }
    }

    public String nextTraceId() {
        return IdUtil.nanoId(10);
    }

    public String getTraceId() {
        return TraceIdContextHolder.get();
    }

    public String getTraceId(HttpServletRequest request) {
        return request != null ? request.getHeader(TRACE_ID) : null;
    }

    public <T, X extends Throwable> T getRetOpt(R<T> result, Supplier<? extends X> supplier) throws X {
        if (result.isSuccess() && result.getData() != null) {
            return result.getData();
        }
        throw supplier.get();
    }

    public String getClientIp() {
        return JakartaServletUtil.getClientIP(getServletRequest());
    }

    public <T extends Annotation> T pointcutGetAnnotation(JoinPoint joinPoint, Class<T> annotationClass) {
        T result = joinPoint.getTarget().getClass().getAnnotation(annotationClass);
        if (result == null) {
            result = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(annotationClass);
        }
        return result;
    }

    public void traceEnabled(Logger logger, Consumer<Logger> consumer) {
        if (logger.isTraceEnabled()) {
            consumer.accept(logger);
        }
    }

    public <T> Consumer<T> consumerVoid() {
        return t -> {
        };
    }
}
